#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>

#define EXIT_SUCCESS 0
#define EXIT_FAILURE 1

static pid_t child_pid;

static void child_handler(int signum)
{
    switch(signum) {
    case SIGALRM:
    	fprintf(stderr, "Child failed to launch (SIGALRM)\n");
    	exit(EXIT_FAILURE);
    	break;
    case SIGUSR1:
    	printf("%d", child_pid);
    	exit(EXIT_SUCCESS);
    	break;
    case SIGCHLD:
    	fprintf(stderr, "Child failed to launch (SIGCHLD)\n");
    	exit(EXIT_FAILURE);
    	break;
    }
}

void daemonize(const char *outfile)
{
    pid_t sid, parent;

    /* Already a daemon */
    if ( getppid() == 1 ) return;

    /* Trap some signals */
    signal(SIGCHLD,child_handler);
	signal(SIGUSR1,child_handler);
	signal(SIGALRM,child_handler);

    /* Fork off the parent process */
    child_pid = fork();
    if (child_pid < 0) {
    	perror("Failed to fork");
        exit(EXIT_FAILURE);
    }

    /* If we got a good PID, then we can exit the parent process. */
    if (child_pid > 0) {
    	/* Wait for confirmation from the child via SIGTERM or SIGCHLD, or
    	   for two seconds to elapse (SIGALRM). pause() should not return. */
    	alarm(2);
    	pause();

    	fprintf(stderr, "Process failed to daemonize within the timeout\n");
        exit(EXIT_FAILURE);
    }

    /* We are now in the child, ie. the daemon */
    parent = getppid();

    /* Cancel certain signals */
    signal(SIGCHLD,SIG_DFL); /* A child process dies */
    signal(SIGTSTP,SIG_IGN); /* Various TTY signals */
    signal(SIGTTOU,SIG_IGN);
    signal(SIGTTIN,SIG_IGN);
    signal(SIGHUP, SIG_IGN); /* Ignore hangup signal */
    signal(SIGTERM,SIG_DFL); /* Die on SIGTERM */

    /* Change the file mode mask */
    umask(0);

    /* Create a new SID for the child process */
    sid = setsid();
    if (sid < 0) {
        exit(EXIT_FAILURE);
    }

    /* Redirect standard files to /dev/null */
    if (!outfile) outfile = "/dev/null";

    if (freopen("/dev/null", "r", stdin) == NULL) {
    	perror("Failed to redirect stdin");
    	exit(EXIT_FAILURE);
    }
    if (freopen(outfile, "w", stdout) == NULL) {
    	perror("Failed to redirect stdout");
    	exit(EXIT_FAILURE);
    }
    if (freopen(outfile, "w", stderr) == NULL) {
    	perror("Failed to redirect stderr");
    	exit(EXIT_FAILURE);
    }

    /* Change the current working directory.  This prevents the current
       directory from being locked; hence not being able to remove it. */
    if ((chdir("/")) < 0) {
    	perror("Failed to change current directory");
        exit(EXIT_FAILURE);
    }

    /* Notify parent that we have successfully daemonized */
    kill(parent, SIGUSR1);
}
